package com.only4play.pcc.service.impl;

import com.only4play.pcc.domain.User;
import com.only4play.pcc.mapper.LikeMapper;
import com.only4play.pcc.mapper.UserMapper;
import com.only4play.pcc.service.LikeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;

/**
 * @author liyuncong
 * @version 1.0
 * @file LikeServiceImpl
 * @brief LikeServiceImpl
 * @details LikeServiceImpl
 * @date 2024-06-24
 * <p>
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2024-06-24               liyuncong          Created
 */

@Slf4j
@Service
public class LikeServiceImpl implements LikeService {

    public final static String KEY_FEED_LIKE = "like_list:";
    public final static String KEY_FEED_LIKE_COUNT = "like_count:";
    public final static String KEY_FEED_LIKE_FRIENDS = "like_list_friends:";
    public final static String KEY_FEED_LIKE_OTHERS = "like_list_others:";

    private static final int LATEST_QUERY_LIMIT = 20;

    @Resource
    private LikeMapper likeMapper;
    @Resource
    private UserMapper userMapper;
    /**
     * 装配springBoot默认注入的stringRedisTemplate，名字就叫stringRedisTemplate
     */
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Long islike(Long oid, Long uid) {
        if (oid == null || uid == null) {
            return 0L;
        }
        return likeMapper.islike(oid, uid);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Long like(Long oid, Long uid) {
        if (oid == null || uid == null) {
            return 0L;
        }
        Long liked = likeMapper.like(oid, uid);
        if (liked > 0) {
            ZSetOperations<String, String> feedLikes = this.stringRedisTemplate.opsForZSet();
            ValueOperations<String, String> feedCount = this.stringRedisTemplate.opsForValue();
            feedLikes.add(KEY_FEED_LIKE + oid, String.valueOf(uid), System.currentTimeMillis());
            return feedCount.increment(KEY_FEED_LIKE_COUNT + oid, 1L);
        }
        return 0L;
    }

    @Override
    public List<User> queryLatestLikeUsers(Long oid, int defaultLimit) {
        if (defaultLimit > LATEST_QUERY_LIMIT || defaultLimit == 0) {
            defaultLimit = LATEST_QUERY_LIMIT;
        }
        return likeMapper.queryLatestLikeUsers(oid, defaultLimit);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Long unlike(Long oid, Long uid) {
        if (oid == null || uid == null) {
            return 0L;
        }
        Long liked = likeMapper.unlike(oid, uid);
        if (liked > 0) {
            ZSetOperations<String, String> feedLikes = this.stringRedisTemplate.opsForZSet();
            ValueOperations<String, String> feedCount = this.stringRedisTemplate.opsForValue();
            feedLikes.remove(KEY_FEED_LIKE + oid, String.valueOf(uid), System.currentTimeMillis());
            return feedCount.decrement(KEY_FEED_LIKE_COUNT + oid, 1L);
        }
        return 0L;
    }

    @Override
    public Long count(Long oid) {
        if (oid == null) {
            return 0L;
        }
        ValueOperations<String, String> feedCount = this.stringRedisTemplate.opsForValue();
        String result = feedCount.get(KEY_FEED_LIKE_COUNT + oid);
        if (!StringUtils.hasText(result)) {
            // if cache empty, query for db and set to cache
            Long count = likeMapper.count(oid);
            feedCount.set(KEY_FEED_LIKE_COUNT + oid, String.valueOf(count));
            return count;
        }
        return Long.parseLong(result);
    }

    @Override
    public List<User> list(Long oid, Long uid, Long cursor, Integer limit, Integer isFriend) {
        // isFriend and uid are not currently supported
        if (isFriend == 0) {
            return likeMapper.list(oid, cursor, limit);
        } else {
            // query user id list, and then get users by ids
            List<Long> userIds = this.likeUserIdsWithFriendsFirst(oid, uid, cursor, limit);
            if (CollectionUtils.isEmpty(userIds)) {
                return Collections.emptyList();
            }
            return userMapper.queryAllByIds(userIds);
        }
    }

    private List<Long> likeUserIdsWithFriendsFirst(Long oid, Long uid, Long cursor, Integer limit) {
        // TODO
        // 初步思考，
        // 1：先用好友关系uid集合和点赞用户uid集合做交集，求出点赞好友列表
        // 2：在用点赞用户uid集合和点赞好友列表集合做差集，求出点赞非好友列表
        // 3：先对好友列表进行分页查询，查询完成后，在对非好友列表进行分页查询
        // 注释：难点在于对巨量数据进行交集、差集计算；
        //      还有就是对游标的控制，比如limit是20，好友列表最后一页数据为10，因此需要在非好友列表补齐10个数据，
        //      可一对游标进行处理，用标志为控制下一组该查询好友还是非好友
        return Collections.emptyList();
    }

}
